package com.hotel.common.user.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.jwt.JWTUtil;
import com.alipay.api.domain.ScheduleTimeResult;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hotel.common.config.RabbitKeyConfig;
import com.hotel.common.config.RedisKeyConfig;
import com.hotel.common.param.*;
import com.hotel.common.util.JwtUtil;
import com.hotel.common.vo.R;
import com.hotel.common.user.entity.Log;
import com.hotel.common.user.entity.User;
import com.hotel.common.user.mapper.LogMapper;
import com.hotel.common.user.mapper.UserMapper;
import com.hotel.common.user.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jcajce.spec.MQVParameterSpec;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.hotel.common.config.RedisKeyConfig.LOGIN_UID_TOKEN;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author mz
 * @since 2024-06-15
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {


    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Autowired
    private LogMapper logMapper;


    @Autowired
    private RedisTemplate redisTemplate;



    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 1.判断参数是否为空
     * 2.判断验证码是否匹配 redis
     * 3.判断手机号是否重复  账号是否重复
     * 4.密码加密 插入user表
     * 5.记录用户日志
     * 6.删除注册验证码
     *
     * @param userAdd
     * @return 注册  ！！
     */
    @Override
    public R register(HttpServletRequest request, UserAdd userAdd) {

        // 1. 判断参数是否为空
        if (userAdd == null || StringUtils.isAnyEmpty(userAdd.getAccount(), userAdd.getCode(), userAdd.getPhone(), userAdd.getPassword())) {

            return R.fail("参数错误");
        }
        // 2.判断验证码是否匹配 redis
        String redisCode = stringRedisTemplate.opsForValue().get(RedisKeyConfig.SMS_RCODE + userAdd.getPhone());
        if (!userAdd.getCode().equals(redisCode)){
            return R.fail("验证码错误");
        }

        // 3. 判断手机号是否重复
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("phone", userAdd.getPhone());

        List<User> userList = userMapper.selectList(userQueryWrapper);
        if (userList != null && userList.size() > 0) {
            return R.fail("手机号重复");
        }

        // 加入密码
        User user = new User();
        user.setPhone(userAdd.getPhone())
                .setAccount(userAdd.getAccount())
                .setStatus(1)
                .setPassword(SecureUtil.md5(userAdd.getPassword()))// 进行密码 加密
                .setCtime(new Date());

        int num = userMapper.insert(user);
        if (num <= 0) {
            return R.fail("注册失败");
        }
        // 5.记录用户日志
        Log log = new Log();

        log.setUserId(user.getId())
                .setMobile(user.getPhone())
                .setInfo(user.getPhone() + "用户注册")
                .setType("用户注册")
                .setIp(request.getRemoteHost())
                .setCtime(new Date());

        logMapper.insert(log);

//        6.删除注册验证码
        stringRedisTemplate.delete(RedisKeyConfig.SMS_RCODE + userAdd.getPhone());

        // 7.积分增加 发送信息
        // 创建一个名为 mqMsg 的 MqMsg 对象，用于构建消息。

        MqMsg mqMsg = new MqMsg();
        mqMsg.setId(IdUtil.getSnowflakeNextId());

        // 5是代表的类型  代表成长值变化
        mqMsg.setType(5);

        GrowthAdd growthAdd = new GrowthAdd();
        growthAdd.setType("新用户注册");
        growthAdd.setGrowth(20);
        growthAdd.setUid(user.getId());

        mqMsg.setMsg(growthAdd);

        // 通过 RabbitMQ 发送 mqMsg 消息到名为 Q_USER_GROWTH 的队列中
        rabbitTemplate.convertAndSend("",RabbitKeyConfig.Q_USER_GROWTH,mqMsg);

        return R.ok("注册成功");
    }


    /**
     * 登录  ！！ 账号 + 密码
     *
     * @param userLogin
     * @return
     */

    /**
     * 使用 账号 + 密码登录
     * 1.判断参数是否为空
     * 2.判断账户是存在，可用
     * 3.判断登录次数是否超过三次
     * 4.验证用户 密码
     * 5.生成token
     * 6.存储token
     * 7.记录登录日志
     * 8.返回token
     * @param userLogin
     * @return
     */

    @Override
    public R loginpass(HttpServletRequest request,UserLogin userLogin) {

        // 1. 判断账号 参数是否为空
        if (userLogin == null || StringUtils.isAnyEmpty(userLogin.getName(), userLogin.getPass())) {
            return R.fail("参数为空");
        }

        // 2.  查询账户是否存在
        String account = userLogin.getName();  // 把得到的账户 和 密码 赋给 账号登录中的   账户和密码
        String password = userLogin.getPass();

        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("account",account);

        List<User> userList = userMapper.selectList(userQueryWrapper);
        if (userList == null && userList.size()==0){

            return R.fail("该账户不存在");
        }

        // 3. 判断账户是否登录超过3次

        User user = userList.get(0);
        // 以 LOGIN_UID_TOKEN 开头，后面跟着用户ID和任意字符的键。 keys 方法将返回一个 Set 集合，其中包含所有匹配该模式的键。
     Set<String> keys =  stringRedisTemplate.keys(LOGIN_UID_TOKEN + user.getId()+"*");
     if (keys != null && keys.size()>=100){
         return R.fail("该用户登录超过了100次");
     }

     //  * 4.验证用户 密码
        if (!user.getPassword().equals(SecureUtil.md5(password))){
            return R.fail("账号或密码错误");
        }

        // 5. 生成token
        String jwt = JwtUtil.createJwt(user.getId(),account,user.getPhone());

        // 6. 存储 TOKEN
        stringRedisTemplate.opsForValue().set(RedisKeyConfig.LOGIN_UID_TOKEN+user.getId()+":"+jwt,"",12*60*60, TimeUnit.SECONDS);

        redisTemplate.opsForValue().set(RedisKeyConfig.LOGIN_TOKEN+jwt,user,12*60*60, TimeUnit.SECONDS);


        // 7.积分增加 发送信息
        // 创建一个名为 mqMsg 的 MqMsg 对象，用于构建消息。
        MqMsg mqMsg = new MqMsg();
        mqMsg.setId(IdUtil.getSnowflakeNextId());
        mqMsg.setType(5);

        Date today = new Date();
       SimpleDateFormat dateFormat =  new SimpleDateFormat("yyyy-MM-dd");
       String todayStr = dateFormat.format(today);

       // 查询今天登录的日志

     List<Log> userLoginLogsToday =   logMapper.selectLoginTime(user.getId(),today);
     if (userLoginLogsToday.isEmpty()){
         GrowthAdd growthAdd = new GrowthAdd();

         growthAdd.setUid(user.getId());
         growthAdd.setType("该用户是首次登录");
         growthAdd.setGrowth(2);
// 创建一个 GrowthAdd 对象 growthAdd，并设置其属性：
         // 用户 ID（user.getId()）、类型（"新用户注册"）和增加的积分数（2）。
         mqMsg.setMsg(growthAdd);

// 通过 RabbitMQ 发送 mqMsg 消息到名为 Q_USER_GROWTH 的队列中。
         rabbitTemplate.convertAndSend("", RabbitKeyConfig.Q_USER_GROWTH,mqMsg);
     }

        // 7. 存储日志
        Log log = new Log();
        log.setCtime(new Date())
        .setUserId(user.getId())
                .setMobile(user.getPhone())
                .setInfo(user.getPhone()+"用户登录")
                .setIp(request.getRemoteHost())
                .setType("用户登录");
              //  .setIp()
        logMapper.insert(log);

        return R.ok(jwt);

    }


    //1.前端提交 手机号   验证码
    //
    //2.后端 接收到账号 密码
    //
    //3.判断  数据是否为空
    //
    //4.判断账号是否存在  是否可用
    //
    //5.判断手机号 和 验证码 是否对应
    //
    //6  判断当前手机号的在线次数是否超过 三次
    //
    //7.生成token   一个字符串 包含用户信息（id,phone,account）
    //
    //     <token,用户信息>               记录用户登录的token
    //
    //           <phone+token,“xxx”>       登录一次记录一次             账户只可以 三个账号在线
    //
    //8.记录用户日志 登录
    //
    //9.返回token

    @Override
    public R logincode(HttpServletRequest request, UserLogin userLogin) {

        if (userLogin == null || StringUtils.isAnyEmpty(userLogin.getName(),userLogin.getPass())){
            return R.fail("参数错误");
        }

        //2.查询手机号是否存在
        String phone = userLogin.getName();

        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("phone",phone);

        List<User> userList = userMapper.selectList(userQueryWrapper);
        if (userList == null && userList.size() == 0){
            return R.fail("手机号不存在");

        }

        String rediscode = stringRedisTemplate.opsForValue().get(RedisKeyConfig.SMS_LCODE + phone);
        if (!rediscode.equals(userLogin.getPass()) || !phone.equals(userLogin.getName())){
            return R.fail("手机号或密码错误");
        }

        // 3. 判断是否超过三次
        User user = userList.get(0);
        Set<String> keys = stringRedisTemplate.keys(LOGIN_UID_TOKEN + phone + "*");
        if (keys !=null && keys.size()>=100){
            return R.fail("用户登录超过100次");
        }

        // 5. 生成token
        String jwt = JwtUtil.createJwt(user.getId(),phone,user.getPhone());

        // 6. 存储 TOKEN
        stringRedisTemplate.opsForValue().set(RedisKeyConfig.LOGIN_UID_TOKEN+user.getId()+":"+jwt,"",12*60*60, TimeUnit.SECONDS);

        redisTemplate.opsForValue().set(RedisKeyConfig.LOGIN_TOKEN+jwt,user,12*60*60, TimeUnit.SECONDS);


        // 记录日志
        Log log = new Log();
        log.setCtime(new Date())
                .setId(user.getId())
                .setMobile(user.getPhone())
                .setInfo(user.getPhone() + "用户登录")
                .setIp(request.getRemoteHost())
                .setType("用户登录");
        logMapper.insert(log);

        return R.ok(jwt);
    }


    /**
     *
     * 找回密码  ！！
     * @param restPwd
     * @return
     */
    @Override
    public R reset(RestPwd restPwd) {
            // 1. 先判断账号参数是否为空
            if (restPwd == null || StringUtils.isAnyEmpty(restPwd.getPhone(), restPwd.getCode(),restPwd.getNewPassword(),restPwd.getConfirmPassword())) {
                return R.fail("参数错误");
            }

            // 2. 查询手机号是否匹配

            String phone = restPwd.getPhone();

            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
            userQueryWrapper.eq("phone", phone);

            User user = userMapper.selectOne(userQueryWrapper);
            if (user == null){
                return R.fail("手机号未注册");
            }

            String newPassword = restPwd.getNewPassword();  // 新密码来自用户输入
            user.setPassword(SecureUtil.md5(newPassword));  //设置新密码  加入 md5 加密


        // 4. 设置新密码（此处应先加密新密码）

            int updateResult =  userMapper.updateById(user);
            if (updateResult <= 0){
                return R.fail("密码重置失败");
            }

            Log log = new Log();
            log.setCtime(new Date())
                    .setType("重置密码")
                    .setMobile(user.getPhone())
                    .setInfo(user.getPhone() + "重置密码成功")
                    .setId(user.getId());

            logMapper.insert(log);
            return R.ok("密码重置成功");

    }


    /**
     * 退出登录
     * 1.从header中获取token
     *      * 2.更具token 获取用户信息
     *      * 3.删除改用相关的token、
     *      * 4. 响应前端
     * @param token
     * @return
     */
    @Override
    public R logout(String token) {

        User user = (User)redisTemplate.opsForValue().get(RedisKeyConfig.LOGIN_TOKEN + token);

        // 删除 相关token
        redisTemplate.delete(RedisKeyConfig.LOGIN_TOKEN + token);
        stringRedisTemplate.delete(RedisKeyConfig.LOGIN_TOKEN + user.getId()+":"+token);
        return R.ok("退出账号");

    }

    @Override
    public R findUserInfoById(Integer id) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("id",id);
        User user = userMapper.selectOne(userQueryWrapper);
        if (user != null) {
            return R.ok(user);
        }
        return R.fail("获取失败");
    }


}
